/**
 * Capable of writing out an XML report, by delegating the call to
 * generateReport() to an extended TestNG reporter.
 */
package com.googlecode.lightest.core

import groovy.xml.DOMBuilder

import javax.xml.transform.stream.StreamResult
import javax.xml.transform.stream.StreamSource
import javax.xml.transform.TransformerFactory

import org.apache.xml.serialize.OutputFormat 
import org.apache.xml.serialize.XMLSerializer
import org.testng.ISuite
import org.testng.xml.XmlSuite
import org.testng.reporters.FailedReporter
import org.testng.reporters.XMLUtils

class LightestReporter extends LightestTestListener {
    static final XSL_NS = 'http://www.w3.org/1999/XSL/Transform'
    
    def xmlReporter
    def failedReporter
    
    LightestReporter() {
        xmlReporter = new XMLReporter2()
        failedReporter = new FailedReporter()
    }
    
    /**
     * Generates a report in the specified output directory. This will include
     * the standard testng-results.xml, testng-failed.xml, as well as a framed
     * HTML report.
     *
     * @param xmlSuites
     * @param suites
     * @param outputDirectory
     */
    void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites,
        String outputDirectory)
    {
        def outputDir = new File(outputDirectory)
        
        xmlReporter.setTaskResultMap(getTaskResultMap())
        xmlReporter.generateReport(xmlSuites, suites, outputDirectory)
        failedReporter.generateReport(xmlSuites, suites, outputDirectory)
        
        copyResources(outputDir)
        createHtmlReport(outputDir, new File(outputDir, 'testng-results.xml'))
    }
    
    void copyResources(File outputDir) {
        def resources = [
            'jquery-1.2.6.min.js',
            'lightest-report.css',
            'lightest-report.js'
        ]
        
        for (resource in resources) {
            def stream = this.class.getResourceAsStream("/${resource}")
            def file = new File(outputDir, resource)
            file.text = stream.text
        }
    }
    
    /**
     * Transforms the testng-results.xml file to a user-friendly HTML report.
     * This implementation comes from the testng-xslt maven plugin mojo.
     * 
     * @param outputDir     the output directory specified when running the
     *                      TestNG tests
     * @param tetsNGResult  the testng-results.xml file to transform
     * 
     * @see <a href="http://code.google.com/p/testng-xslt/">TestNG XSLT</a>
     */
    protected void createHtmlReport(File outputDir, File testNGResult) {
        System.setProperty('javax.xml.transform.TransformerFactory',
            'net.sf.saxon.TransformerFactoryImpl')
            
        def factory = TransformerFactory.newInstance()
        def resultSource = new StreamSource(testNGResult)
        def os = new FileOutputStream(new File(outputDir, 'index.html'))
        def transformer = factory.newTransformer(getTransformSource())
        
        transformer.setParameter('testNgXslt.outputDir', outputDir.getPath())
        transformer.setParameter('testNgXslt.reportTitle', 'Lightest Results')
        transformer.transform(resultSource, new StreamResult(os))
    }
     
    /**
     * Returns the TestNG XSLT as a StreamSource, with task-oriented
     * modifications. 
     */
    protected StreamSource getTransformSource() {
        def is = getClass().getResourceAsStream('/testng-results.xsl')
        def reader = new InputStreamReader(is)
        def doc = DOMBuilder.parse(reader)
        def root = doc.documentElement
        def head = doc.getElementsByTagName('head').item(0)
        
        def outputs = doc.getElementsByTagNameNS(XSL_NS, 'output')
        outputs.each { it.setAttribute('indent', 'no') }
        
        def link = doc.createElement('link')
        link.setAttribute('rel', 'stylesheet')
        link.setAttribute('href', 'lightest-report.css')
        head.appendChild(link)
        
        def script = doc.createElement('script')
        script.setAttribute('type', 'text/javascript')
        script.setAttribute('src', 'jquery-1.2.6.min.js')
        head.appendChild(script)
        
        script = doc.createElement('script')
        script.setAttribute('type', 'text/javascript')
        script.setAttribute('src', 'lightest-report.js')
        head.appendChild(script)
            
        def divs = doc.getElementsByTagName('div')
        divs.each { div ->
            if (div.getAttribute('class') == 'testMethodDetails') {
                def forEach = doc.createElementNS(XSL_NS, 'xsl:for-each')
                forEach.setAttribute('select', 'task-result')
                
                def apply = doc.createElementNS(XSL_NS, 'xsl:call-template')
                apply.setAttribute('name', 'top-level-task-result')
                
                forEach.appendChild(apply)
                div.appendChild(forEach)
            }
        }
        
        def templates = doc.getElementsByTagNameNS(XSL_NS, 'template')
        for (template in templates) {
            if (template.getAttribute('name') == 'powered-by') {
                def div = doc.createElement('div')
                div.setAttribute('style', 'height: 1000px;')
                template.appendChild(div)
                break
            }
        }
        
        is = getClass().getResourceAsStream('/lightest-report.xsl')

        def tempFile = File.createTempFile('lightest-report', '.xsl')
        tempFile.deleteOnExit()
        tempFile.text = is.text
        
        def include = doc.createElementNS(XSL_NS, 'xsl:include')
        include.setAttribute('href', tempFile.toURI().toString())
        root.appendChild(include)
        
        def format = new OutputFormat(doc);
        def combinedFile = File.createTempFile('combined', '.xsl')
        def os = new FileOutputStream(combinedFile)
        def serializer = new XMLSerializer(os, format);

        format.setIndenting(true);
        serializer.serialize(doc);
        combinedFile.deleteOnExit()
        
        return new StreamSource(combinedFile)
    }
}
